package com.lastproject.web.bean;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.model.ArrayDataModel;
import javax.faces.model.DataModel;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import com.lastproject.dto.BookDTO;
import com.lastproject.dto.BorrowedDTO;
import com.lastproject.dto.DomainDTO;
import com.lastproject.dto.RatingDTO;
import com.lastproject.dto.ReviewDTO;
import com.lastproject.dto.SugestionDTO;
import com.lastproject.dto.UserDTO;
import com.lastproject.dto.WaitingListDTO;
import com.lastproject.ejb.dao.BookDAORemote;
import com.lastproject.ejb.dao.BorrowedDAORemote;
import com.lastproject.ejb.dao.CheckPermissionDAORemote;
import com.lastproject.ejb.dao.DomainDAORemote;
import com.lastproject.ejb.dao.RatingDAORemote;
import com.lastproject.ejb.dao.ReviewContentDAORemote;
import com.lastproject.ejb.dao.ReviewDAORemote;
import com.lastproject.ejb.dao.SugestionDAORemote;
import com.lastproject.ejb.dao.TimerLibraryBeanRemote;
import com.lastproject.ejb.dao.UserDAORemote;
import com.lastproject.ejb.dao.WaitingListDAORemote;
import com.lastproject.ejb.exceptions.NoPermissionException;

@ManagedBean
@SessionScoped
public class BookTableData implements Serializable {

	private static final String BOOK_PROCESSING = "processing";

	private static final String BOOK_NOT_AVAILABLE = "notAvailable";

	private static final String BOOK_AVAILABLE = "available";

	/**
	 * 
	 */
	private static final long serialVersionUID = 1646875970957532283L;

	private String status;
	private boolean editable = false;
	private DataModel<BookBean> filterModel = null;
	private BookBean[] books;
	private int currentUser;
	private String testYear;
	private List<UserDTO> users;
	private List<BorrowedDTO> historyBorroweds;

	// ///
	private DataModel<BookBean> filterModelUser = null;
	private BookBean[] booksUser;
	private String successMessage = "";
	private String textToSearch = "";
	private boolean deletion = false;
	private int idForDeletion = 0;
	private boolean deletionReview = false;
	private ReviewDTO reviewForDelete;
	private Integer usersBookSize = 0;
	private Integer usersWaitingListSize = 0;
	private Integer booksSize = 0;

	private DataModel<BookBean> filterModelUsersBook = null;
	private DataModel<BookBean> filterModelUsersWaitingList = null;

	private String reviewText = "";
	private String sugestionBookName = "";
	private String sugestionBookAuthor = "";
	private String suggestionInfoMessage = "";
	private String error = "";
	private String errorDomain = "";
	private String errorAddBook = "";

	private BookBean currentBookBean;

	private BookDTO myBook;
	private BookBean bookBean;
	private List<BorrowedDTO> borroweds;
	private List<WaitingListDTO> waitingLists;
	private DomainDTO domain;

	private String noRights;

	private BookDTO bookToBeReviewed;

	private String nrRows = "5";

	@EJB
	TimerLibraryBeanRemote timerLibraryBeanRemote;

	@EJB
	WaitingListDAORemote waitingListDAORemote;

	@EJB
	BorrowedDAORemote borrowedDAORemote;

	@EJB
	UserDAORemote userDAORemote;

	@EJB
	BookDAORemote bdr;

	@EJB
	DomainDAORemote ddr;

	@EJB
	RatingDAORemote ratingDAORemote;

	@EJB
	SugestionDAORemote sugestionDAORemote;

	@EJB
	ReviewDAORemote reviewDAORemote;

	@EJB
	CheckPermissionDAORemote checkPermissionDAOremote;

	@EJB
	ReviewContentDAORemote rcDAORemote;

	/**
	 * Default Constructor
	 */
	public BookTableData() {
	}

	/**
	 * This method fills the data models for the tables in the "Search" and
	 * "Manage Books" menus.
	 * <p>
	 * The rules for filling the table in the "Search" menu are:
	 * <ul>
	 * <li>For the current user the following books will be listed:
	 * <ul>
	 * <li>the ones that are NOT reserved and NOT on the waiting list. For these
	 * books, the "Reserve" button will be active and the "Waiting List" button
	 * will be inactive.</li>
	 * <li>the ones that are reserved by other users or are on the waiting list
	 * for other users. For these books, the "Reserve" button will be inactive
	 * and the "Waiting List" button will be active.</li>
	 * </ul>
	 * </li>
	 * </ul>
	 */
	@PostConstruct
	public void init() {

		UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
				.getExternalContext().getSessionMap().get("userDTO");
		errorDomain = "";
		errorAddBook = "";
		error = "";
		deletion = false;
		this.deletionReview = false;
		reviewForDelete = null;

		List<BookDTO> booksList = bdr.getBooksNotInWaitingList(userDTO.getId());
		booksUser = new BookBean[booksList.size()];

		bookBean = new BookBean();
		for (int i = 0; i < booksList.size(); i++) {
			int currentRating = 0;
			BookDTO bookDTO = booksList.get(i);
			// the current book cannot be a borrowed book
			List<RatingDTO> ratings = ratingDAORemote.getAllByBookId(bookDTO
					.getId());
			List<RatingDTO> ratingsByUser = ratingDAORemote
					.getAllByUserIdAndBookId(userDTO.getId(), bookDTO.getId());
			for (RatingDTO ratingDTO : ratings) {
				currentRating = currentRating + ratingDTO.getRating();
			}
			if (ratings.size() != 0)
				currentRating = currentRating / ratings.size();
			DomainDTO domain = (DomainDTO) ddr.findByID(bookDTO.getIdDomain());
			booksUser[i] = new BookBean(bookDTO);
			booksUser[i].setRating(currentRating);
			booksUser[i].setDomain(domain);
			setRatingUser(booksUser, i, ratingsByUser);

			String bookStatus = bookDTO.getStatus();
			setAvailability(i, bookStatus);
		}
		filterModelUser = new BookFilterModel(new ArrayDataModel<BookBean>(
				booksUser));

		getUsersBooks();
		getUsersWaitingList();

		//
		myBook = new BookDTO();
		domain = new DomainDTO();
		borroweds = new ArrayList<BorrowedDTO>();
		List<BookDTO> list = bdr.getAll();
		books = new BookBean[list.size()];

		for (int i = 0; i < list.size(); i++) {
			users = new ArrayList<UserDTO>();
			BookDTO bookDTO = list.get(i);// instead of finding the user in DB,
											// we can return an object with the
											// user
			waitingLists = waitingListDAORemote.getByIdBook(bookDTO.getId());
			for (WaitingListDTO waitingList : waitingLists) {
				UserDTO user = userDAORemote.findById(waitingList.getIdUser());
				users.add(user);
			}
			borroweds = borrowedDAORemote.getRecentBook(bookDTO.getId());
			DomainDTO domain = (DomainDTO) ddr.findByID(bookDTO.getIdDomain());
			books[i] = new BookBean(bookDTO);
			books[i].setDomain(domain);
			books[i].setBorroweds(borroweds);
			books[i].setWaitingLists(waitingLists);
			books[i].setUsers(users);
		}
		filterModel = new BookFilterModel(new ArrayDataModel<>(books));
	}

	/**
	 * Sets the rating of a book given by the users.
	 * 
	 * @param bookBeans
	 *            a books array
	 * @param index
	 *            the index of the book in the array for which the rating
	 *            applies
	 * @param ratingsByUser
	 *            list of ratings for all the books.
	 */
	private void setRatingUser(BookBean[] bookBeans, int index,
			List<RatingDTO> ratingsByUser) {
		bookBeans[index].setRatingUser(ratingsByUser.isEmpty());
	}

	/**
	 * Sets the availability for borrowing and the availability for queuing a
	 * book.
	 * 
	 * @param index
	 *            the index of the book.
	 * @param bookStatus
	 *            the status of the book.
	 */
	private void setAvailability(int index, String bookStatus) {
		switch (bookStatus) {
		case BOOK_AVAILABLE:
			booksUser[index].setAvailableToBorrow(false);
			booksUser[index].setAvailableToQueue(true);
			break;
		case BOOK_NOT_AVAILABLE:
		case BOOK_PROCESSING:
			booksUser[index].setAvailableToBorrow(true);
			booksUser[index].setAvailableToQueue(false);
			break;
		}
	}

	public void preRenderView(ComponentSystemEvent event) throws IOException {

		if (!checkPermissionDAOremote.hasLibraryRights(true)) {
			FacesContext fc = FacesContext.getCurrentInstance();
			ExternalContext ec = fc.getExternalContext();

			ec.redirect("restrictedAccess.xhtml");

		}
	}

	public void preRenderViewAccess(ComponentSystemEvent event) {
		if (!checkPermissionDAOremote.hasLibraryManagementRights(true)) {
			FacesContext fc = FacesContext.getCurrentInstance();
			ExternalContext ec = fc.getExternalContext();
			try {
				ec.redirect("restrictedAccess.xhtml");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public String getTestYear() {
		if (myBook.getYear() == 0)
			return "";
		else
			return "" + myBook.getYear();
	}

	/**
	 * metoda preia atributele inserate pe pagina pentru a le introduce in baza
	 * de date tot aici sunt facute si validarile, iar daca totul este bine,
	 * cartea e adaugata
	 * 
	 * @param actionEvent
	 * @return mesaj informativ
	 */

	public String addBook(ActionEvent actionEvent) {

		if (myBook.getName().equals(""))
			return errorAddBook = "Empty name";
		if (myBook.getAuthor().equals(""))
			return errorAddBook = "Empty author";
		if (myBook.getName().equals(0))
			return errorAddBook = "Empty year";
		try {
			myBook.setYear(Integer.parseInt(testYear));
		} catch (Exception e) {
			return errorAddBook = "Year must be between 1900 and 2014";
		}
		if (myBook.getYear() < 1900 || myBook.getYear() > 2014)
			return errorAddBook = "Year must be between 1900 and 2014";
		BookDTO bookDTO = new BookDTO();
		bookDTO.setId(myBook.getId());
		bookDTO.setName(myBook.getName());
		bookDTO.setAuthor(myBook.getAuthor());
		bookDTO.setIdDomain(myBook.getIdDomain());
		bookDTO.setStatus(BOOK_AVAILABLE);
		bookDTO.setYear(myBook.getYear());
		BorrowedDTO borrowedDTO = new BorrowedDTO();
		borrowedDTO.setBorrowDate(null);
		borrowedDTO.setMustReturnDate(null);
		borrowedDTO.setReturnDate(null);
		borrowedDTO.setIdBook(myBook.getId());
		boolean ok = bdr.create(true, bookDTO);
		if (!ok) {
			return errorAddBook = "You do not have the permission to add books!";
		}
		myBook.setId(0);
		myBook.setAuthor("");
		myBook.setIdDomain(1);
		myBook.setYear(0);
		myBook.setName("");
		init();
		return errorAddBook = "Book Successfully added";

	}

	/**
	 * metoda este folosita pentru ca un user sa returneze o carte
	 * 
	 * @param id
	 *            id-ul cartii
	 * @return mesaj informativ
	 */

	public String returned(int id) {
		BorrowedDTO borrowDTO = new BorrowedDTO();
		Date date = new Date();
		borroweds = borrowedDAORemote.getByReturnDate(id);
		borrowDTO = borroweds.get(0);
		date = new Date();
		borrowDTO.setReturnDate(date);
		BookDTO book = bdr.findById(id);
		List<WaitingListDTO> remainigItemsInWL = waitingListDAORemote
				.getByIdBook(book.getId());
		if (remainigItemsInWL.isEmpty()) {
			book.setStatus(BOOK_AVAILABLE);
		} else {
			book.setStatus(BOOK_PROCESSING);
		}
		borrowedDAORemote.edit(borrowDTO);

		boolean ok = bdr.edit(true, book);
		if (!ok) {
			return error = "You do not have the right to mark a book as returned!";
		}

		init();

		/** emilian */
		timerLibraryBeanRemote.stopTimer("returnTimer_" + borrowDTO.getId());

		return error = "Successfully returned!";
	}

	/**
	 * metoda este folosita pentru a vizualiza istoricul unei carti
	 * 
	 * @param actionEvent
	 */

	public void viewHistory(ActionEvent actionEvent) {
		update();
		int id = (int) actionEvent.getComponent().getAttributes().get("bookId");
		historyBorroweds = borrowedDAORemote.getAllByBook(id);
		for (BorrowedDTO borrowed : historyBorroweds) {
			borrowed.setBorrowedBy(userDAORemote.findById(borrowed.getIdUser()));
		}
	}

	/**
	 * Get the book to be reviewed and save the info in the backing bean.
	 * 
	 * @param actionEvent
	 *            the action event through which the ID of the book is
	 *            submitted.
	 */
	public void setReviewBook(ActionEvent actionEvent) {
		int id = (int) actionEvent.getComponent().getAttributes()
				.get("bookIdForReview");
		BookDTO book = rcDAORemote.getById(id);
		setBookToBeReviewed(book);
	}

	/**
	 * Checks if for a certain book a critic's review was submitted.
	 * 
	 * @param book
	 *            the book to be checked.
	 * @return true, if there exists a critic's review, false, otherwise.
	 */
	public boolean containsCristicsReview(String id) {
		if (null == id || id.isEmpty()) {
			return false;
		}
		int intId = Integer.parseInt(id);
		return rcDAORemote.hasContent(intId) == 1 ? true : false;
	}

	/**
	 * metoda este folosita pentru a imporumuta o carte
	 * 
	 * @param id
	 *            id-ul cartii
	 * @return mesaj informativ
	 */

	public String borrow(int id) {
		waitingLists = waitingListDAORemote.getByIdBook(id);
		if (waitingLists.size() == 0)
			return error = "No user selected!";
		BorrowedDTO borrowDTO = new BorrowedDTO();
		Date date = new Date();
		borrowDTO.setBorrowDate(date);
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.DATE, 14); // add 14 days
		date = cal.getTime();
		bookBean = filterModel.getRowData();
		// System.out.println("current user: " +bookBean.getCurrentUser());
		borrowDTO.setMustReturnDate(date);
		borrowDTO.setIdUser(bookBean.getCurrentUser());
		BookDTO book = bdr.findById(id);
		UserDTO user = userDAORemote.findById(bookBean.getCurrentUser());
		// sendMailForInformation(user.getWorkEmail(), book.getName());
		borrowDTO.setIdBook(id);
		bookBean.setCurrentUser(id);
		book.setStatus(BOOK_NOT_AVAILABLE);
		borrowedDAORemote.create(borrowDTO);
		boolean ok = bdr.edit(true, book);
		if (!ok) {
			return error = "You do not have the permission to borrow books!";
		}
		waitingListDAORemote.remove(id, borrowDTO.getIdUser());

		/** emilian: */
		Date today = new Date();

		Calendar calendarToday = Calendar.getInstance();

		System.out.println(calendarToday);
		Calendar tomorrow = Calendar.getInstance();
		tomorrow.setTime(today);

		tomorrow.add(Calendar.DATE, 13);

		System.out.println(today + " " + tomorrow);

		System.out.println("bookTableData.borrow milisecunde: ");
		System.out.println((tomorrow.getTimeInMillis() - calendarToday
				.getTimeInMillis()) * 13);

		borrowDTO = borrowedDAORemote.getByIdUserIdBookDate(user.getId(),
				book.getId(), today);

		timerLibraryBeanRemote.startTimer("returnTimer_" + borrowDTO.getId(),
				10000);

		/** emilian */

		init();
		return error = "Successfully borrowed!";
	}

	/**
	 * metoda este folosita pentru a avertiza userul ca urmeaza sa stearga o
	 * carte
	 * 
	 * @param id
	 *            id-ul cartii care este stearsa
	 * @return mesaj informativ
	 */

	public void del(int id) {
		deletion = true;
		idForDeletion = id;
		error = "";
	}

	/**
	 * metoda este folosita pentru a sterge o carte din baza de date
	 * 
	 * @param id
	 *            id-ul cartii care este stearsa
	 * @return mesaj informativ
	 */

	public String delete() {
		if (bdr.findById(idForDeletion) != null) {
			try {

				boolean ok = bdr.removeBook(true, idForDeletion);
				if (!ok) {
					return error = "You do not have the right to delete books!";
				}
			} catch (Exception e) {
				deletion = false;
				return error = "The book cannot be deleted!";
			}

		} else {
			return error = "Cannot delete!";
		}
		init();
		return "";
	}

	/**
	 * metoda este folosita pentru a trimite unui user mail de informare desprea
	 * cartea dorita
	 * 
	 * @param email
	 *            mail-ul userului
	 * @param bookName
	 *            numele cartii
	 */

	public void sendMailForInformation(String email, String bookName) {

		try {

			FacesContext f = FacesContext.getCurrentInstance();
			UserDTO userDTO = (UserDTO) f.getExternalContext().getSessionMap()
					.get("userDTO");
			Properties props = new Properties();
			props.put("mail.smtp.host", "msgc1i.msg.de");
			Session session = Session.getInstance(props, null);
			MimeMessage msg = new MimeMessage(session);

			msg.setFrom(new InternetAddress(userDTO.getWorkEmail()));

			msg.setRecipient(Message.RecipientType.TO, new InternetAddress(
					email));

			msg.setSubject("Come for book");
			msg.setText("Hello, please come after book: " + bookName);
			Transport.send(msg);
		} catch (MessagingException ex) {
			ex.printStackTrace();
		}

	}

	/**
	 * metoda este folosita pentru a cauta o carte sau mai multe
	 * 
	 * @param actionEvent
	 */

	public void searchBook(ActionEvent actionEvent) {
		myBook = new BookDTO();
		borroweds = new ArrayList<BorrowedDTO>();
		deletion = false;
		errorDomain = "";
		errorAddBook = "";
		error = "";
		List<BookDTO> list = bdr.getBooksContainingText(textToSearch.trim());
		books = new BookBean[list.size()];
		int i = 0;
		for (BookDTO bookDTO : list) {
			users = new ArrayList<UserDTO>();
			waitingLists = waitingListDAORemote.getByIdBook(bookDTO.getId());
			for (WaitingListDTO waitingList : waitingLists) {
				UserDTO user = userDAORemote.findById(waitingList.getIdUser());
				users.add(user);
			}
			borroweds = borrowedDAORemote.getRecentBook(bookDTO.getId());
			DomainDTO domain = (DomainDTO) ddr.findByID(bookDTO.getIdDomain());
			books[i] = new BookBean(bookDTO);
			books[i].setDomain(domain);
			books[i].setBorroweds(borroweds);
			books[i].setWaitingLists(waitingLists);
			books[i].setUsers(users);
			i++;

		}

		textToSearch = "";
		filterModel = new BookFilterModel(new ArrayDataModel<>(books));
	}

	/**
	 * metoda este folosita pentru a trimite mesaj informativ catre userul
	 * selectat
	 * 
	 * @param id
	 *            id-ul cartii
	 * @param userId
	 *            id-ul userului
	 * @return mesaj informativ
	 */

	public String sendInformation(int id) {
		waitingLists = waitingListDAORemote.getByIdBook(id);
		if (waitingLists.size() == 0)
			return error = "No user selected!";
		bookBean = filterModel.getRowData();
		// System.out.println("current user: " +bookBean.getCurrentUser());
		BookDTO book = bdr.findById(id);
		book.setStatus(BOOK_PROCESSING);
		boolean ok = bdr.edit(true, book);
		if (!ok) {
			return error = "You do not have the permission to send this kind of email!";
		}
		UserDTO user = userDAORemote.findById(bookBean.getCurrentUser());
		sendMailForInformation(user.getWorkEmail(), book.getName());
		init();

		return error = "An email has been sent to user: " + user.getFirstname()
				+ " " + user.getLastname();
	}

	/**
	 * metoda este folosita folosita pentru a adauga un domain daca domeniul
	 * deja exista acesta nu va fii adaugat in baza de date
	 * 
	 * @return mesaj informativ
	 */

	public String addDomain() {
		if (domain.getName().equals(""))
			return errorDomain = "No domain name added";
		DomainDTO myDomain = new DomainDTO();
		List<DomainDTO> listDomains = ddr.findAll();
		for (DomainDTO domainDTO : listDomains) {
			if (domainDTO.getName().equalsIgnoreCase(domain.getName().trim())) {
				domain.setName("");
				return errorDomain = "Domain already existent!";
			}
		}
		myDomain.setName(domain.getName());
		ddr.create(myDomain);

		init();
		return errorDomain = "Domain added successfully";
	}

	/**
	 * trimite e-mail administratorului bibliotecii. salveaza in baza de date un
	 * waiting list (daca e permisiune)
	 */
	public void informNeedBook(ActionEvent event) {

		successMessage = "";

		/** obtin bean-ul si cartea curenta */
		BookBean bookBean = (BookBean) event.getComponent().getAttributes()
				.get("currentBookBean");
		BookDTO bookDTO = bookBean.getBook();

		/** iau userul de pe sesiune */
		UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
				.getExternalContext().getSessionMap().get("userDTO");

		/** obtin bibliotecarul */
		UserDTO librarian = userDAORemote.getLibrarian();
		try {

			if (librarian == null) {
				/** daca nu e nici un user cu rol de bibliotecar */
				successMessage = "There is no library admin";
				return;
			}

			if (!waitingListDAORemote.getByBookIdOtherUser(bookDTO.getId(),
					userDTO.getId())) {
				successMessage = "The book cannot be reserved, it might have been reserved by somebody else.";
				return;
			}
			// set the status of the book to "processing"
			bookDTO.setStatus(BOOK_PROCESSING);
			bdr.edit(true, bookDTO);

			createWaitingList(bookDTO, userDTO);
			sendEmailToLibrarian(bookBean, bookDTO, userDTO, librarian);

			successMessage = "An email has been sent to library admin";
		} catch (MessagingException ex) {
			ex.printStackTrace();
			successMessage = "There was a problem at email sending";
		} catch (NoPermissionException ne) {
			ne.printStackTrace();
			successMessage = "You have no permission to borrow books";
		}

		setTextToSearch("");
		searchUsersBooks();
	}

	/**
	 * adauga userul curent in lista de asteptare a cartii pe care a ales-o
	 * 
	 * @param bookDTO
	 *            - cartea selectata
	 * @param userDTO
	 *            - user-ul care doreste
	 * 
	 * @throws NoPermissionException
	 *             - in caz ca nu are permisune
	 */
	private void createWaitingList(BookDTO bookDTO, UserDTO userDTO)
			throws NoPermissionException {
		WaitingListDTO wlDto = new WaitingListDTO();
		wlDto.setDate(new Date());
		wlDto.setIdBook(bookDTO.getId());
		wlDto.setIdUser(userDTO.getId());

		waitingListDAORemote.create(true, wlDto);
	}

	/**
	 * trimite un email bibliotecarului in numele userului curent spunand ce
	 * carte doreste si daca e urgent
	 * 
	 * @param bookBean
	 *            - bean-ul cartii
	 * @param bookDTO
	 *            - cartea dorita
	 * @param userDTO
	 *            - userul care doreste
	 * @param librarian
	 *            - bibliotecarul
	 * @throws MessagingException
	 *             - exceptie la e-mail
	 * @throws AddressException
	 *             - exceptie la e-mail
	 */
	private void sendEmailToLibrarian(BookBean bookBean, BookDTO bookDTO,
			UserDTO userDTO, UserDTO librarian) throws MessagingException,
			AddressException {
		Properties props = new Properties();
		props.put("mail.smtp.host", "msgc1i.msg.de");

		Session session = Session.getInstance(props, null);
		MimeMessage msg = new MimeMessage(session);

		msg.setFrom(new InternetAddress(userDTO.getWorkEmail()));
		msg.setRecipient(Message.RecipientType.TO, new InternetAddress(
				librarian.getWorkEmail()));

		msg.setSubject("Need Book");
		StringBuilder stringBuilderMessageText = new StringBuilder();

		stringBuilderMessageText
				.append("Hello! Please add me to the waiting list for the book ");
		stringBuilderMessageText.append(bookDTO.getName());
		stringBuilderMessageText.append(", written by ");
		stringBuilderMessageText.append(bookDTO.getAuthor());
		stringBuilderMessageText.append(", from year ");
		stringBuilderMessageText.append(bookDTO.getYear() + ".\n");

		// if (bookBean.isEmergency() == true) {
		// stringBuilderMessageText.append("It's an emergency. \n");
		// }

		stringBuilderMessageText.append("Thanks, ");
		stringBuilderMessageText.append(userDTO.getFirstname() + " "
				+ userDTO.getLastname());

		msg.setText(stringBuilderMessageText.toString());

		System.out.println("email sent");
		/**
		 * trimiterea mesajului, e comentat sa nu se faca spam
		 */
		Transport.send(msg);
	}

	/**
	 * obtine toate cartile care sunt imprumutate de userul curent
	 * 
	 * @return
	 */
	public DataModel<BookBean> getUsersBooks() {

		UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
				.getExternalContext().getSessionMap().get("userDTO");

		List<BookDTO> list1 = bdr.getBooksBorrowedByUser(userDTO.getId());
		setUsersBookSize(list1.size());
		booksUser = new BookBean[list1.size()];
		int i = 0; // TODO: change the for structure
		for (BookDTO bookDTO : list1) {
			DomainDTO domain = (DomainDTO) ddr.findByID(bookDTO.getIdDomain());
			booksUser[i] = new BookBean(bookDTO);
			booksUser[i].setDomain(domain);
			i++;
		}

		filterModelUsersBook = new BookFilterModel(
				new ArrayDataModel<BookBean>(booksUser));

		return filterModelUsersBook;
	}

	public DataModel<BookBean> getUsersWaitingList() {

		UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
				.getExternalContext().getSessionMap().get("userDTO");

		List<BookDTO> list1 = bdr
				.getBooksWithUserInWaitingList(userDTO.getId());

		setUsersWaitingListSize(list1.size());

		booksUser = new BookBean[list1.size()];
		int i = 0;
		for (BookDTO bookDTO : list1) {
			DomainDTO domain = (DomainDTO) ddr.findByID(bookDTO.getIdDomain());
			booksUser[i] = new BookBean(bookDTO);
			booksUser[i].setDomain(domain);
			i++;
		}

		filterModelUsersWaitingList = new BookFilterModel(
				new ArrayDataModel<BookBean>(booksUser));
		return filterModelUsersWaitingList;
	}

	public String refreshBooks() {
		init();
		successMessage = "";
		return "bookReviews.xhtml";
	}

	public void refreshMyBooks() {
		init();
		getUsersBooks();
		getUsersWaitingList();
	}

	/**
	 * obtine toate cartile la care este userul curent pe lista de asteptare
	 * 
	 * @return
	 */

	public void informDontNeedBook(ActionEvent event) {

		// System.out.println(event.getComponent().getClientId());

		/** cartea curenta */
		BookBean bookBean = (BookBean) event.getComponent().getAttributes()
				.get("currentUserBookBean");

		BookDTO bookDTO = bookBean.getBook();

		UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
				.getExternalContext().getSessionMap().get("userDTO");

		try {
			waitingListDAORemote.remove(bookDTO.getId(), userDTO.getId());
			bookDTO.setStatus(BOOK_AVAILABLE);
			bdr.edit(true, bookDTO);
			searchUsersBooks();

		} catch (NullPointerException e) {
			e.printStackTrace();
		}

		// repopulate books after remove from waiting list.
		getUsersWaitingList();
	}

	/**
	 * metoda este folosita pentru a insera noul rating al cartii luate de pe
	 * randul curent
	 * 
	 * @return
	 */

	public String newRating() {
		RatingDTO myRating = new RatingDTO();

		noRights = "";
		UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
				.getExternalContext().getSessionMap().get("userDTO");

		BookBean bookBean = filterModelUser.getRowData();
		myRating.setIdBook(bookBean.getBook().getId());
		myRating.setIdUser(userDTO.getId());
		myRating.setRating(bookBean.getRating());

		boolean ok = ratingDAORemote.create(true, myRating);
		if (!ok) {
			noRights = "You do not have the permission to rate books!";
		}
		init();
		return error = "searchBooks.xhtml";
	}

	public List<SugestionDTO> getSugestions() {
		return sugestionDAORemote.findAllSortedByTitle();
	}

	/**
	 * obtine toate recenziile unei carti si le pune in bean-ul cartii
	 * 
	 * @return pagina bookReviews.xhtml
	 */
	public String bookReviews() {
		this.currentBookBean = filterModelUser.getRowData();

		List<ReviewDTO> reviewsDTO = reviewDAORemote
				.getReviewsByBookId(currentBookBean.getBook().getId());

		currentBookBean.setReviews(reviewsDTO);

		return "bookReviews.xhtml";
	}

	public void getCriticsReview(ActionEvent event) {
		BookBean bookBean = (BookBean) event.getComponent().getAttributes()
				.get("currentBook");
		BookDTO bookDTO = bookBean.getBook();
		byte[] content = rcDAORemote.getContent(bookDTO);

		FacesContext facesContext = FacesContext.getCurrentInstance();
		ExternalContext externalContext = facesContext.getExternalContext();
		externalContext.setResponseHeader("Content-Type", "application/pdf");
		externalContext.setResponseHeader(
				"Content-Disposition",
				"attachment;filename=\"Cristic's Review for "
						+ bookDTO.getName() + " by " + bookDTO.getAuthor()
						+ ", " + bookDTO.getYear() + ".pdf\"");
		try {
			externalContext.getResponseOutputStream().write(content);
		} catch (IOException e) {
			e.printStackTrace();
		}
		facesContext.responseComplete();
	}

	/**
	 * salveaza recenzia
	 */
	public String saveReview() {
		if (!reviewText.trim().isEmpty()) {

			UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
					.getExternalContext().getSessionMap().get("userDTO");

			ReviewDTO reviewDTO = new ReviewDTO();
			reviewDTO.setText(reviewText.trim());
			reviewDTO.setUserId(userDTO.getId());
			reviewDTO.setBookId(currentBookBean.getBook().getId());

			noRights = "";
			boolean ok = reviewDAORemote.create(true, reviewDTO);
			if (!ok) {
				noRights = "You do not have the right to review books!";
				return "bookReviews.xhtml";
			}

			currentBookBean.setReviews(reviewDAORemote
					.getReviewsByBookId(currentBookBean.getBook().getId()));
		}

		this.deletionReview = false;
		reviewForDelete = null;
		reviewText = "";
		return "";
	}

	/**
	 * inregistreaza o noua sugestie de carte
	 */
	public String sugestBook() {
		if (!sugestionBookName.trim().isEmpty()
				&& !sugestionBookAuthor.trim().isEmpty()) {
			UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
					.getExternalContext().getSessionMap().get("userDTO");

			suggestionInfoMessage = "";

			SugestionDTO sugestionDTO = new SugestionDTO();
			sugestionDTO.setSubject(sugestionBookName);
			sugestionDTO.setMessage(sugestionBookAuthor);
			sugestionDTO.setUserId(userDTO.getId());

			noRights = "";
			boolean ok = sugestionDAORemote.create(true, sugestionDTO);
			if (!ok) {
				noRights = "You do not have the right to suggest a book!";
				return "bookSugestion.xhtml";
			}

			clearSugetsion();

			suggestionInfoMessage = "suggestion sent";

		}
		return "";
	}

	/**
	 * curata camputile de la sugestie
	 */
	public void clearSugetsion() {
		this.sugestionBookAuthor = "";
		this.sugestionBookName = "";
		this.suggestionInfoMessage = "";
	}

	/**
	 * curata textArea de la recenzie
	 */
	public void cancelReview() {
		setReviewText("");
		this.deletionReview = false;
		reviewForDelete = null;
	}

	/**
	 * sterge recenzie. Fiecare user isi poate sterge recenziile lui
	 * 
	 * @param event
	 */
	public String deleteReview() {
		if (reviewForDelete != null) {

			boolean ok = reviewDAORemote.remove(true, reviewForDelete);
			noRights = "";
			if (!ok) {
				noRights = "You do not have the right to delete book reviews!";
				return "bookReviews.xhtml";
			}

			currentBookBean.setReviews(reviewDAORemote
					.getReviewsByBookId(currentBookBean.getBook().getId()));

			reviewForDelete = null;
		}
		this.deletionReview = false;
		return "";
	}

	/**
	 * repopuleaza tabelul cu cartile care contin in titlu stringul din
	 * textToSearch, si nu sunt pe lista de asteptare
	 */
	public String searchUsersBooks() {

		UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
				.getExternalContext().getSessionMap().get("userDTO");

		List<BookDTO> list = bdr.getBooksContainingTextAndNotInWaitingList(
				userDTO.getId(), textToSearch.trim());

		setBooksSize(list.size());
		booksUser = new BookBean[list.size()];
		Integer currentRating;
		for (int i = 0; i < list.size(); i++) {
			BookDTO bookDTO = list.get(i);
			currentRating = new Integer(0);
			List<RatingDTO> ratings = ratingDAORemote.getAllByBookId(bookDTO
					.getId());
			List<RatingDTO> ratingsByUser = ratingDAORemote
					.getAllByUserIdAndBookId(userDTO.getId(), bookDTO.getId());

			for (RatingDTO ratingDTO : ratings) {
				currentRating = currentRating + ratingDTO.getRating();
			}
			if (ratings.size() != 0)
				currentRating = currentRating / ratings.size();

			DomainDTO domain = (DomainDTO) ddr.findByID(bookDTO.getIdDomain());
			booksUser[i] = new BookBean(bookDTO);
			booksUser[i].setRating(currentRating);
			booksUser[i].setDomain(domain);
			if (ratingsByUser.isEmpty())
				booksUser[i].setRatingUser(false);
			else
				booksUser[i].setRatingUser(true);
			setAvailability(i, bookDTO.getStatus());
		}

		filterModelUser = new BookFilterModel(new ArrayDataModel<BookBean>(
				booksUser));

		textToSearch = "";
		successMessage = "";
		return "bookSearch.xhtml";
	}

	/**
	 * metoda de actune pt butoanele back to books
	 * 
	 * @return
	 */
	public String backToBooks() {
		setTextToSearch("");
		searchUsersBooks();
		clearSugetsion();
		this.deletionReview = false;
		reviewForDelete = null;
		return "bookSearch.xhtml";
	}

	/**
	 * metoda folosita la lansarea paginii bookUser.xhtml incarca tabelul cu
	 * cartile imprumutate de utilizator si cel cu cartile pt care userul e pe
	 * lista de asteptare
	 * 
	 * @return
	 */
	public String startBookUser() {

		getUsersBooks();
		getUsersWaitingList();
		this.deletionReview = false;
		reviewForDelete = null;

		return "bookUser.xhtml";
	}

	/**
	 * metoda folosita la lansarea paginii bookSearch.xhtml incarca tabelul
	 * cartilor cu datele din baza de date (tabelul cu cartile din biblioteca,
	 * nu cartile imprumutate de utilizatorul curent)
	 * 
	 * @return
	 */
	public String startBookSearch() {
		update();
		getBooksUser();
		this.deletionReview = false;
		reviewForDelete = null;
		return "bookSearch.xhtml";
	}

	/**
	 * metoda folosita la lansarea pagini bookCRUD.xhtml
	 * 
	 * reinitializeaza tabelul, populandu-l cu datele actuale in bd F
	 * 
	 * @return
	 */
	public String startBookCRUD() {

		update();
		this.deletionReview = false;
		reviewForDelete = null;
		return "bookCRUD.xhtml";
	}

	/**
	 * metoda este folosita pentru a face update pe toate listele dupa ce s-a
	 * facut vreo modificare
	 */
	public void update() {
		int i = 0;
		errorDomain = "";
		error = "";
		errorAddBook = "";
		deletion = false;
		myBook = new BookDTO();
		domain = new DomainDTO();
		borroweds = new ArrayList<BorrowedDTO>();
		List<BookDTO> list = bdr.getAll();
		books = new BookBean[list.size()];
		for (BookDTO bookDTO : list) {
			users = new ArrayList<UserDTO>();
			waitingLists = waitingListDAORemote.getByIdBook(bookDTO.getId());
			for (WaitingListDTO waitingList : waitingLists) {
				UserDTO user = userDAORemote.findById(waitingList.getIdUser());
				users.add(user);
			}
			borroweds = borrowedDAORemote.getRecentBook(bookDTO.getId());
			DomainDTO domain = (DomainDTO) ddr.findByID(bookDTO.getIdDomain());
			books[i] = new BookBean(bookDTO);
			books[i].setDomain(domain);
			books[i].setBorroweds(borroweds);
			books[i].setWaitingLists(waitingLists);
			books[i].setUsers(users);
			i++;
		}
		filterModel = new BookFilterModel(new ArrayDataModel<>(books));
	}

	/**
	 * intreaba daca sigur vrei sa stergi review
	 */
	public void sureDeleteReview(ActionEvent event) {
		deletionReview = true;
		ReviewDTO reviewDTO = (ReviewDTO) event.getComponent().getAttributes()
				.get("currentReview");
		reviewForDelete = reviewDTO;
	}

	/**
	 * in caz de raspuns nu la intrebarea esti sigur sa stergi review
	 */
	public void setDeletionReviewFalse() {
		this.deletionReview = false;
		reviewForDelete = null;
	}

	public void putUserOnWaitingList(ActionEvent event) {
		BookBean bookBean = (BookBean) event.getComponent().getAttributes()
				.get("currentBookBean");
		UserDTO userDTO = (UserDTO) FacesContext.getCurrentInstance()
				.getExternalContext().getSessionMap().get("userDTO");
		try {
			createWaitingList(bookBean.getBook(), userDTO);
		} catch (NoPermissionException e) {
			e.printStackTrace();
			successMessage = "You are not permitted to perform this action!";
		}
		searchUsersBooks();
	}

	// GETTERS & SETTERS

	public String getNrRows() {
		return nrRows;
	}

	public void setNrRows(String nrRows) {
		this.nrRows = nrRows;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public String getNoRights() {
		return noRights;
	}

	public void setNoRights(String noRights) {
		this.noRights = noRights;
	}

	public DataModel<BookBean> getBooksUser() {
		return filterModelUser;
	}

	public void setFilterModelUser(DataModel<BookBean> filterModelUser) {
		this.filterModelUser = (BookFilterModel) filterModelUser;
	}

	public String getTextToSearch() {
		return textToSearch;
	}

	public void setTextToSearch(String textToSearch) {
		this.textToSearch = textToSearch;
	}

	public List<BorrowedDTO> getBorroweds() {
		return borroweds;
	}

	public void setBorroweds(List<BorrowedDTO> borroweds) {
		this.borroweds = borroweds;
	}

	public BookDTO getMyBook() {
		return myBook;
	}

	public void setMyBook(BookDTO myBook) {
		this.myBook = myBook;
	}

	public void setTestYear(String testYear) {
		this.testYear = testYear;
	}

	public List<DomainDTO> getDomains() {
		return ddr.findAll();
	}

	public List<UserDTO> getUsers() {
		return userDAORemote.getAll();
	}

	public void setUsers(List<UserDTO> users) {
		this.users = users;
	}

	public void setDomains(List<DomainDTO> domains) {
	}

	public int getIdForDeletion() {
		return idForDeletion;
	}

	public void setIdForDeletion(int idForDeletion) {
		this.idForDeletion = idForDeletion;
	}

	public DataModel<BookBean> getBooks() {
		return filterModel;
	}

	public void setAuths(BookBean[] books) {
		this.books = books;
	}

	public boolean isEditable() {
		return editable;
	}

	public void setEditable(boolean editable) {
		this.editable = editable;
	}

	public DataModel<BookBean> getFilterModel() {
		return filterModel;
	}

	public void setFilterModel(DataModel<BookBean> filterModel) {
		this.filterModel = filterModel;
	}

	public void setBooks(BookBean[] books) {
		this.books = books;
	}

	public boolean isDeletion() {
		return deletion;
	}

	public void setDeletion(boolean deletion) {
		this.deletion = deletion;
	}

	public List<WaitingListDTO> getWaitingLists() {
		return waitingLists;
	}

	public void setWaitingLists(List<WaitingListDTO> waitingLists) {
		this.waitingLists = waitingLists;
	}

	public String getError() {
		return error;
	}

	public void setError(String error) {
		this.error = error;
	}

	public DomainDTO getDomain() {
		return domain;
	}

	public void setDomain(DomainDTO domain) {
		this.domain = domain;
	}

	public int getCurrentUser() {
		return currentUser;
	}

	public void setCurrentUser(int currentUser) {
		this.currentUser = currentUser;
	}

	public List<BorrowedDTO> getHistoryBorroweds() {
		return historyBorroweds;
	}

	public void setHistoryBorroweds(List<BorrowedDTO> historyBorroweds) {
		this.historyBorroweds = historyBorroweds;
	}

	public String getSuccesMessage() {
		return successMessage;
	}

	public Integer getUsersBookSize() {
		return usersBookSize;
	}

	public void setUsersBookSize(Integer usersBookSize) {
		this.usersBookSize = usersBookSize;
	}

	public void setSuccesMessage(String succesMessage) {
		this.successMessage = succesMessage;
	}

	public Integer getUsersWaitingListSize() {
		return usersWaitingListSize;
	}

	public void setUsersWaitingListSize(Integer usersWaitingListSize) {
		this.usersWaitingListSize = usersWaitingListSize;
	}

	public Integer getBooksSize() {
		return booksSize;
	}

	public void setBooksSize(Integer booksSize) {
		this.booksSize = booksSize;
	}

	public String getReviewText() {
		return reviewText;
	}

	public void setReviewText(String reviewText) {
		this.reviewText = reviewText;
	}

	public String getSugestionBookName() {
		return sugestionBookName;
	}

	public void setSugestionBookName(String sugestionBookName) {
		this.sugestionBookName = sugestionBookName;
	}

	public String getSugestionBookAuthor() {
		return sugestionBookAuthor;
	}

	public void setSugestionBookAuthor(String sugestionBookAuthor) {
		this.sugestionBookAuthor = sugestionBookAuthor;
	}

	public String getSugestionInfoMessage() {
		return suggestionInfoMessage;
	}

	public void setSugestionInfoMessage(String sugestionInfoMessage) {
		this.suggestionInfoMessage = sugestionInfoMessage;
	}

	public BookBean getCurrentBookBean() {
		return currentBookBean;
	}

	public void setCurrentBookBean(BookBean currentBookBean) {
		this.currentBookBean = currentBookBean;
	}

	public void setBooksUser(BookBean[] booksUser) {
		this.booksUser = booksUser;
	}

	public String getErrorDomain() {
		return errorDomain;
	}

	public void setErrorDomain(String errorDomain) {
		this.errorDomain = errorDomain;
	}

	public String getErrorAddBook() {
		return errorAddBook;
	}

	public void setErrorAddBook(String errorAddBook) {
		this.errorAddBook = errorAddBook;
	}

	public boolean isDeletionReview() {
		return deletionReview;
	}

	public void setDeletionReview(boolean deletionReview) {
		this.deletionReview = deletionReview;
	}

	public ReviewDTO getReviewForDelete() {
		return reviewForDelete;
	}

	public void setReviewForDelete(ReviewDTO reviewForDelete) {
		this.reviewForDelete = reviewForDelete;
	}

	public BookBean getBookBean() {
		return bookBean;
	}

	public void setBookBean(BookBean bookBean) {
		this.bookBean = bookBean;
	}

	public BookDTO getBookToBeReviewed() {
		return bookToBeReviewed;
	}

	public void setBookToBeReviewed(BookDTO bookToBeReviewed) {
		this.bookToBeReviewed = bookToBeReviewed;
	}

}
